/*
 *  Copyright (C) 2010 Jonathan MERCIER <bioinfornatics@fedoraproject.org>,
 *  Louise-Amélie SCHMITT <louise-amelie.schmitt@etu.u-bordeaux1.fr>,
 *  Florence Maurier <florence_maurier@orange.fr>,
 *  Marianne Dussart <marianne.dussart@orange.fr>,
 *  Nicolas Fontaine <nifontaine@gmail.com>
 *
 *  JACoMode is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  JACoMode is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with JACoMode.  If not, see <http://www.gnu.org/licenses/>.
 */
package fr.ubdx1.jacomode.models;

import java.util.Iterator;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Enumeration;
import org.miv.graphstream.graph.Edge;
import org.miv.graphstream.graph.Node;
import org.miv.graphstream.graph.implementations.DefaultGraph;

/**
 *
 * @author Louise-Amélie SCHMITT
 */
public class GraphMotifs extends DefaultGraph /*implements java.io.Serializable*/ {

    private static final long serialVersionUID = 1L;
    private String css = "graph {color:grey; text-size:10; padding:20;}"
	    + "node {node-shape:circle; text-align:aside; text-color:black; border-color:black; border-width:1px;}"
	    + "node.click {color:#B0E064;}"
	    + "node.active {color:#FF7272;}"
	    + "edge {color:grey;}"
	    + "edge.click {color:#B0E064;}"
	    + "edge.active {color:#FF7272;}";

    public GraphMotifs(boolean strict_checking, boolean auto_create)
    {
	setStrictChecking(strict_checking);
	setAutoCreate(auto_create);
	// Linking the stylesheet to the graph
	addAttribute("ui.stylesheet", css);

    }

    public void PackGraph()
    {
	// Provide basic info to the graph before beginning display
	// IMPORTANT: if not called before rendering, the graph will be uncomplete
	SetCoord();
	SetThickness();
    }

    private void TagNode(Node node, String motifName)
    {
	// Creating for each node a list of the motifs it belongs to
	if (node.hasAttribute("motifs")) {
	    String previousAttribute = (String) node.getAttribute("motifs");
	    String[] attList = previousAttribute.split(",");
	    boolean found = false;
	    for (int i = 0; i < attList.length; i++) {
		if (attList[i] == null ? motifName == null : attList[i].equals(motifName)) {
		    found = true;
		}
	    }
	    if (!found) {
		node.changeAttribute("motifs", previousAttribute + "," + motifName);
	    }
	} else {
	    node.addAttribute("motifs", motifName);
	}
    }

    private void TagEdge(Edge edge, String motifName)
    {
	// Creating for each edge a list of the motifs it belongs to
	if (edge.hasAttribute("motifs")) {
	    String previousAttribute = (String) edge.getAttribute("motifs");
	    String[] attList = previousAttribute.split(",");
	    boolean found = false;
	    for (int i = 0; i < attList.length; i++) {
		if (attList[i] == null ? motifName == null : attList[i].equals(motifName)) {
		    found = true;
		}
	    }
	    if (!found) {
		edge.changeAttribute("motifs", previousAttribute + "," + motifName);
	    }
	} else {
	    edge.addAttribute("motifs", motifName);
	}
    }

    public Integer AddMotif(String motifName, String motifReactions)
    {
	Integer count = 0;
	// Parsing each reaction list to create the edges
	// (the nodes are created automatically when an edge they're linked to is created)
	String previousReaction = "";
	String[] reactionList = motifReactions.split(" ");

	if (reactionList.length == 1) {
	    if (motifReactions.substring(0, 1).equals("(")) {
		    motifReactions = motifReactions.substring(1);
		    motifReactions = motifReactions.substring(0, motifReactions.length() - 1);
		}
	    Node currentNode = addNode(motifReactions);
	    TagNode(currentNode, motifReactions);
	    currentNode.addAttribute("label", motifReactions);
	} else {
	    for (int i = 0; i < reactionList.length; i++) {
		String currentReaction = reactionList[i];

		if (currentReaction.substring(0, 1).equals("(")) {
		    currentReaction = currentReaction.substring(1);
		    currentReaction = currentReaction.substring(0, currentReaction.length() - 1);
		}

		if (!previousReaction.isEmpty()) {
		    Edge currentEdge = getEdge(previousReaction.concat(currentReaction));
		    if (getEdge(previousReaction.concat(currentReaction)) == null)
		    {
			currentEdge = addEdge(previousReaction.concat(currentReaction), previousReaction, currentReaction);
		    }
		    TagEdge(currentEdge, motifName);
		    currentEdge.getNode0().addAttribute("label", previousReaction);
		    TagNode(currentEdge.getNode0(), motifName);
		    currentEdge.getNode1().addAttribute("label", currentReaction);
		    TagNode(currentEdge.getNode1(), motifName);
		    count++;
		}
		previousReaction = currentReaction;

	    }
	}
	return count;
    }

    private void SetCoord()
    {
	// Sets the nodes' position

	Iterator<? extends Node> nodes = getNodeIterator();

	double yMax = 0.0;

	// First: set the degree of each node as it's y value, and keep the max degree (yMax)
	while (nodes.hasNext()) {
	    Node currentNode = nodes.next();
	    int degree = currentNode.getDegree();
	    currentNode.addAttribute("y", degree);
	    if (yMax < degree) {
		yMax = (double) degree;
	    }
	}

	Hashtable nodeList = new Hashtable(0);

	nodes = getNodeIterator();

	// Second: Sort the nodes in a hashtable for which the degree is the key
	// and the value is an arraylist of the nodes sharing the same degree
	while (nodes.hasNext()) {
	    Node currentNode = nodes.next();
	    int i = (Integer) (currentNode.getAttribute("y"));
	    if (!nodeList.containsKey(i)) {
		nodeList.put(i, new ArrayList<Node>(0));
	    }
	    ((ArrayList) nodeList.get(i)).add(currentNode);
	}

	Enumeration keys = nodeList.elements();

	// Third: Browse the hashtable to set each node's x value as a fair share
	// of an arbitrary width (here, the max degree)
	while (keys.hasMoreElements()) {
	    ArrayList currentArray = (ArrayList) keys.nextElement();
	    System.out.println(currentArray);
	    int arraySize = currentArray.size();
	    for (int i = 0; i < arraySize; i++) {
		Node currentNode = (Node) currentArray.get(i);
		System.out.println(i);
		double x = 0;
		if (arraySize > 1) {
		    x = i * (yMax / (arraySize - 1)) - yMax / 2;
		}
		System.out.println(x);
		currentNode.setAttribute("x", x);
	    }
	}

    }

    private void SetThickness()
    {
	// Setting the thickness of each node's border and edge's width
	// according to the number of motifs it belongs to

	int nbMotifs;

	Iterator<? extends Node> nodes = getNodeIterator();

	while (nodes.hasNext()) {
	    Node currentNode = nodes.next();
	    String motifsCovered = (String) currentNode.getAttribute("motifs");
	    nbMotifs = motifsCovered.split(",").length;
	    int thickness = nbMotifs * 3;
	    currentNode.addAttribute("ui.style", "width:" + thickness + "px;");
	}

	Iterator<? extends Edge> edges = getEdgeIterator();

	while (edges.hasNext()) {
	    Edge currentEdge = edges.next();
	    String motifsCovered = (String) currentEdge.getAttribute("motifs");
	    nbMotifs = motifsCovered.split(",").length;
	    int thickness = nbMotifs * 2 - 1;
	    currentEdge.addAttribute("ui.style", "width:" + thickness + ";");
	    // TEST:
	    currentEdge.addAttribute("label", nbMotifs);
	}

    }
}
